<script lang="ts" setup>
import {
  assignMenuPermission,
  getMenuTree,
  getRoleMenuList
} from "@/api/modules/system";
import type {
  MenuTree,
  RoleMenuAssignParams
} from "@/api/modules/system/types";
import { ElMessage, ElTree } from "element-plus";
import { PlusColumn, PlusDrawerForm } from "plus-pro-components";
import { computed, nextTick, onMounted, ref, watch } from "vue";

defineOptions({
  name: "role-menu-assign"
});

const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },
  roleInfo: {
    type: Object,
    required: true
  }
});

const emit = defineEmits(["close", "success"]);

const visible = ref(props.visible);
const loading = ref(false);
const menuTree = ref<MenuTree[]>([]);
const selectedMenuIds = ref<string[]>([]);
const originalMenuIds = ref<string[]>([]);
const treeRef = ref<InstanceType<typeof ElTree>>();

// 计算属性：选中菜单数量
const selectedCount = computed(() => selectedMenuIds.value.length);

// 监听visible变化，当抽屉打开时加载数据
watch(
  () => props.visible,
  newVal => {
    visible.value = newVal;
    if (newVal && props.roleInfo?.id) {
      loadData();
    }
  }
);

// 监听roleInfo变化，当角色信息变化时重新加载数据
watch(
  () => props.roleInfo,
  newVal => {
    if (visible.value && newVal?.id) {
      loadData();
    }
  },
  { deep: true }
);

const columns: PlusColumn[] = [
  {
    label: "菜单",
    width: 120,
    prop: "menu",
    hasLabel: false
  }
];

// 处理菜单树数据，确保所有节点都有必要的属性
function processMenuTree(nodes: MenuTree[]): MenuTree[] {
  if (!nodes || !Array.isArray(nodes)) return [];

  return nodes.map(node => {
    // 创建节点副本，避免直接修改原始数据
    const processedNode = { ...node };

    // 确保节点有ID
    if (processedNode.id === undefined || processedNode.id === null) {
      processedNode.id = Math.floor(Math.random() * 100000).toString();
    }

    // 确保节点有name属性
    if (!processedNode.name) {
      // 尝试从其他可能的属性中获取名称
      const anyNode = processedNode as any;

      // 优先使用menuName属性，这是API返回的实际菜单名称
      if (anyNode.menuName) {
        processedNode.name = String(anyNode.menuName);
      } else if (anyNode.title) {
        processedNode.name = String(anyNode.title);
      } else if (anyNode.label) {
        processedNode.name = String(anyNode.label);
      } else {
        // 如果没有可用的名称，则设置默认名称
        processedNode.name = `菜单项 ${processedNode.id}`;
      }
    }

    // 递归处理子节点
    if (processedNode.children && Array.isArray(processedNode.children)) {
      processedNode.children = processMenuTree(processedNode.children);
    }

    return processedNode;
  });
}

// 加载所有数据
async function loadData() {
  loading.value = true;
  try {
    // 同时加载菜单树和角色已分配的菜单
    await Promise.all([loadMenuTree(), loadRoleMenus()]);
  } catch (error) {
    ElMessage.error("加载菜单数据失败");
  } finally {
    loading.value = false;
  }
}

// 加载菜单树
async function loadMenuTree() {
  try {
    const res = await getMenuTree();

    if (res.success && res.data) {
      let rawMenuTree: MenuTree[] = Array.isArray(res.data)
        ? res.data
        : [res.data];

      // 处理菜单树数据，确保数据完整性
      menuTree.value = processMenuTree(rawMenuTree);
    }
  } catch (error) {
    ElMessage.error("加载菜单树失败");
  }
}

// 加载角色已分配的菜单
async function loadRoleMenus() {
  if (!props.roleInfo?.id) return;

  try {
    const res = await getRoleMenuList(props.roleInfo.id);

    // 处理不同的数据结构情况
    let roleMenus: MenuTree[] = [];

    if (res.data && Array.isArray(res.data)) {
      // 直接是数组的情况
      roleMenus = res.data;
    } else if (res.data && typeof res.data === "object") {
      // 是对象的情况，尝试获取其中的数组
      if ("records" in res.data && Array.isArray(res.data.records)) {
        roleMenus = res.data.records;
      }
    }

    // 处理角色菜单数据，确保数据完整性
    roleMenus = processMenuTree(roleMenus);

    // 提取菜单ID
    selectedMenuIds.value = extractMenuIds(roleMenus);

    // 保存原始菜单ID，用于比对变更
    originalMenuIds.value = [...selectedMenuIds.value];

    // 设置树的选中状态
    nextTick(() => {
      if (treeRef.value) {
        // 清空当前选中状态
        treeRef.value.setCheckedKeys([], false);
        // 设置新的选中状态
        treeRef.value.setCheckedKeys(selectedMenuIds.value);
      }
    });
  } catch (error) {
    ElMessage.error("加载角色菜单失败");
  }
}

// 从菜单树中提取所有菜单ID
function extractMenuIds(menus: MenuTree[]): string[] {
  const ids: string[] = [];

  function traverse(nodes: MenuTree[]) {
    if (!nodes || !nodes.length) return;

    for (const node of nodes) {
      if (node.id) {
        ids.push(node.id);
      }
      if (node.children && node.children.length > 0) {
        traverse(node.children);
      }
    }
  }

  traverse(menus);
  return ids;
}

// 处理树节点选中状态变化
function handleCheckChange() {
  // 更新选中的菜单ID
  selectedMenuIds.value = treeRef.value.getCheckedKeys(false) as string[];
}

// 提交菜单分配
async function handleSubmit() {
  if (!props.roleInfo?.id) return;

  loading.value = true;
  try {
    // 检查是否有变更
    const hasChanges = !areArraysEqual(
      selectedMenuIds.value,
      originalMenuIds.value
    );

    if (!hasChanges) {
      ElMessage.info("菜单分配未变更");
      handleClose();
      return;
    }

    // 调用API分配菜单
    const params: RoleMenuAssignParams = {
      roleId: props.roleInfo.id,
      menuIds: selectedMenuIds.value
    };

    const res = await assignMenuPermission(params);

    if (res.success) {
      ElMessage.success("菜单分配成功");
      emit("success");
      handleClose();
    } else {
      ElMessage.error(res.message || "菜单分配失败");
    }
  } catch (error) {
    ElMessage.error("菜单分配失败");
  } finally {
    loading.value = false;
  }
}

// 关闭抽屉
function handleClose() {
  // 重置状态
  selectedMenuIds.value = [];
  originalMenuIds.value = [];
  emit("close");
}

// 比较两个数组是否相等
function areArraysEqual(arr1: string[], arr2: string[]): boolean {
  if (arr1.length !== arr2.length) return false;

  const sorted1 = [...arr1].sort();
  const sorted2 = [...arr2].sort();

  return sorted1.every((val, idx) => val === sorted2[idx]);
}

// 全选菜单
function selectAllMenus() {
  if (treeRef.value) {
    // 获取所有节点
    const allNodes = getAllTreeNodeIds(menuTree.value);

    // 全选
    treeRef.value.setCheckedKeys(allNodes);
    // 更新选中状态
    selectedMenuIds.value = treeRef.value.getCheckedKeys(false) as string[];
  }
}

// 清空选择
function clearSelection() {
  if (treeRef.value) {
    treeRef.value.setCheckedKeys([]);
    selectedMenuIds.value = [];
  }
}

// 获取树的所有节点ID
function getAllTreeNodeIds(tree: any[]): number[] {
  const ids: number[] = [];

  function traverse(nodes: any[]) {
    if (!nodes || !nodes.length) return;

    for (const node of nodes) {
      if (node.id) {
        ids.push(node.id);
      }
      if (node.children && node.children.length > 0) {
        traverse(node.children);
      }
    }
  }

  traverse(tree);
  return ids;
}

// 初始化
onMounted(() => {
  if (visible.value && props.roleInfo?.id) {
    loadData();
  }
});
</script>

<template>
  <div>
    <PlusDrawerForm
      v-model:visible="visible"
      size="25%"
      :form="{ columns }"
      :title="`角色菜单分配: ${props.roleInfo?.name || ''}`"
      :loading="loading"
      @close="handleClose"
      @confirm="handleSubmit"
    >
      <template #plus-field-menu>
        <div v-if="!menuTree || menuTree.length === 0">暂无可分配菜单</div>
        <div v-else class="w-full">
          <!-- 操作按钮 -->
          <div class="menu-actions">
            <div class="menu-buttons">
              <el-button type="primary" size="small" @click="selectAllMenus"
                >全选</el-button
              >
              <el-button type="warning" size="small" @click="clearSelection"
                >清空</el-button
              >
            </div>
            <div class="menu-counter">已选择: {{ selectedCount }} 个菜单</div>
          </div>

          <!-- 菜单树 -->
          <div class="menu-tree">
            <el-tree
              ref="treeRef"
              :data="menuTree"
              node-key="id"
              show-checkbox
              default-expand-all
              highlight-current
              :props="{
                children: 'children',
                label: 'name'
              }"
              @check="handleCheckChange"
            >
              <template #default="{ data }">
                <span class="custom-tree-node">
                  <span class="menu-name">{{ data.name || "未命名" }}</span>
                  <span v-if="data.path" class="menu-path">{{
                    data.path
                  }}</span>
                </span>
              </template>
            </el-tree>
          </div>
        </div>
      </template>
    </PlusDrawerForm>
  </div>
</template>

<style scoped>
.menu-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 5px;
  margin-bottom: 10px;
}

.menu-buttons {
  display: flex;
  gap: 8px;
}

.menu-counter {
  font-size: 14px;
  color: #606266;
}

.menu-tree {
  height: 400px;
  padding: 10px;
  overflow: auto;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}

.custom-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.menu-name {
  font-weight: 500;
}

.menu-path {
  margin-left: 8px;
  font-size: 12px;
  color: #8c8c8c;
}
</style>
